#include "if_utilits.h"
#include <sample_log.h>
#include <common_utilits.h>
#include <algorithm>
#ifdef _WIN32
#include <iostream>
#include <winsock2.h>
#include <stdio.h>   
#include <stdlib.h>
#include <string.h> 
#include <windef.h>   
#include <iphlpapi.h>  
#include <vector>
#include <errno.h>
#pragma comment(lib,"iphlpapi.lib")
#else
#include <stdio.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netdb.h>
#include <ifaddrs.h>
#include <net/if.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <vector>
#include <regex>
#endif

#if __linux__
/**
 * 获取网卡设备MAC/IP地址
 * @param required_name　指定网卡名,未指定则获取第一个非loopback网卡
 * @param addrtype 请求的地址类型:0:MAC地址,1:IP地址
 * @param fmt     返回的数据是否格式化为可见字符串,为true时将MAC地址格式化为16进制MAC地址字符串,IP地址格式化为.分割的十进制IP地址
 * @param addr [out] 返回的MAC/IP地址
 * @param debugLog 是否输出调试信息
 * @return 调用成功返回true,否则返回false
 */
static bool get_if_address(const std::string &required_name, int addrtype, bool fmt, std::string &addr, bool debugLog)
{
	int fd, nic;
	struct ifreq buf[8];
	struct ifconf ifc;
	bool nameMatchRequired = !required_name.empty();
	if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) >= 0)
	{
		gdface::raii fd_guard([]() { close(fd); });/** 自动关闭fd */
		ifc.ifc_len = sizeof(buf);
		ifc.ifc_buf = (caddr_t)buf;
		if (!ioctl(fd, SIOCGIFCONF, (char *)&ifc))
		{
			nic = ifc.ifc_len / sizeof(struct ifreq);
			for (int i = 0; i < nic; ++i)
			{
				if (nameMatchRequired && strcmp(required_name.c_str(), buf[i].ifr_name))
				{
					continue;
				}
				if (ioctl(fd, SIOCGIFFLAGS, (char *)&buf[i]) == 0) {

					if ((buf[i].ifr_flags & IFF_LOOPBACK)) { // don't count loopback
						continue;
					}
					switch (addrtype)
					{
					case 0: 
					{
						if (!(ioctl(fd, SIOCGIFHWADDR, (char *)&buf[i])))
						{
							if (fmt) 
							{
								addr = std::string((char*)buf[i].ifr_hwaddr.sa_data, 6);
							}
							else {
								addr = gdface::bytes_to_hex_string(buf[i].ifr_hwaddr.sa_data, 6);
							}
							SAMPLE_LOG("found network interface {} MAC: {}", buf[i].ifr_name, addr);
							return true;
						}
						break;
					}
					case 1:
					default: 
					{
						if (!(ioctl(fd, SIOCGIFADDR, (char *)&buf[i])))
						{
							auto ipstr = std::string(inet_ntoa(((struct sockaddr_in *)buf[i].ifr_addr)->sin_addr));
							if (fmt)
							{
								addr = ipstr;
							}
							else {
								addr = gdface::parse_ip(ipstr);
							}
							SAMPLE_LOG("found network interface {} IP: {}", buf[i].ifr_name, addr);
							return true;
						}
						break;
					}
					}
					
				}
			}
			SAMPLE_LOG_IF(nameMatchRequired && debugLog, "Not found adapter named: {}", required_name);
		}
	}
	return false;
}
/**
 * 获取网卡设备MAC地址
 * @param mac [out] 返回16进制MAC地址字符串
 * @param required_name　指定网卡名,未指定则获取第一个非loopback网卡
 * @param fmt     返回的数据是否格式化为可见字符串,为true时将IP地址格式化为.分割的十进制IP地址
 * @param debugLog 是否输出调试信息
 * @return 调用成功返回true,否则返回false
 */
static bool get_mac(std::string &mac,const std::string &required_name, bool fmt, bool debugLog)
{
	return get_if_address(required_name, 0, mac,debugLog);
}
/**
 * 获取网卡设备IP地址
 * @param ipaddr [out] 返回.分割十进制IP地址字符串
 * @param required_name　指定网卡名,未指定则获取第一个非loopback网卡
 * @param fmt     返回的数据是否格式化为可见字符串,为true时将IP地址格式化为.分割的十进制IP地址
 * @param debugLog 是否输出调试信息
 * @return 调用成功返回true,否则返回false
 */
static bool get_ipaddr(std::string &ipaddr, const std::string &required_name, bool fmt, bool debugLog)
{
	return get_if_address(required_name, 1, ipaddr, debugLog);
}
//************************************
// 执行command指定的控制台命令,返回结果保存在output,返回执行命令的状态码
// @param    const std::string & command
// @param    bool debugLog 是否输出调试信息
// @param    std::string & output
// @return   int 命令执行成功 执行命令返回的状态码,失败则返回错误代码
//************************************
static int shell_command(const std::string &command, bool debugLog,std::string &output)
{
	if(command.empty())
	{
		SAMPLE_LOG_IF(debugLog, "command is empty");
		return -1;
	}
	FILE   *stream;
	SAMPLE_LOG_IF(debugLog, "command [{}]",command);
	stream = popen(command.c_str(),"r");
	if(!stream)
	{
		SAMPLE_LOG_IF(debugLog, "fail to open pipe");
		return -1;
	}
	/* 输出缓冲区 */
	std::vector<char> buf(1024,0);
	auto readsize = fread(buf.data(), sizeof(char), buf.capacity(), stream); //将刚刚FILE* stream的数据流读取到buf中
	auto msg = std::string(buf.data(), readsize);
	auto code = pclose(stream);
	SAMPLE_LOG_IF(debugLog, "{} exitcode={}",msg,code);
	output = msg;
	return code;
}
//************************************
// 通过执行shell命令("cat /sys/class/net/$ifname/address")获取MAC地址
// @param    const std::string & ifname 网卡名
// @param    bool debugLog 是否输出调试信息
// @param    std::string & mac 返回16进制MAC地址字符串
// @return   bool 调用成功返回true,否则返回false
//************************************
static bool get_mac_by_cat_sys_class_net(const std::string &ifname, bool debugLog, std::string &mac)
{
	std::string output;
	auto cmd = std::string("cat /sys/class/net/").append(ifname).append("/address 2>/dev/null");
	if (shell_command(cmd, debugLog, output))
	{
		return false;
	}
	if (normalize_nic(output, mac, debugLog))
	{
		SAMPLE_LOG_IF(debugLog, "found network interface {} {}", ifname, mac);
		return true;
	}
	return false;
}


/**
* 通过执行shell命令获取MAC地址
* @param mac [out] 返回16进制MAC地址字符串
* @param required_name　指定网卡名,未指定则获取第一个非loopback网卡
* @return 调用成功返回true,否则返回false
*/
static bool get_mac_by_pipe(const std::string required_name, bool debugLog, std::string &mac)
{
	std::string output;
	if (!required_name.empty())
	{
		return get_mac_by_cat_sys_class_net(required_name, debugLog, mac);
	}
	/* 列出所有网卡设备名 */
	if (shell_command("ls /sys/class/net 2>/dev/null", debugLog, output))
	{
		return false;
	}
	/* 排除 lo 的所有网卡设备名称 */
	auto ifnames = std::regex_replace(output, std::regex("lo"), "");
	auto vifnames = gdface::split(ifnames, "\\s+");
	if (vifnames.empty())
	{
		SAMPLE_LOG_IF(debugLog, "NOT FOUND NET INTERFACE");
		return false;
	}
	size_t pos = ifnames.find("eth0");
	if (pos != std::string::npos) {
		return get_mac_by_cat_sys_class_net("eth0", debugLog, mac);
	}
	pos = ifnames.find("wlan0");
	if (pos != std::string::npos) {
		return get_mac_by_cat_sys_class_net("wlan0", debugLog, mac);
	}
	return get_mac_by_cat_sys_class_net(vifnames[0], debugLog, mac);
}
#endif

//************************************
// 归一化MAC地址字符串转大写并去除分隔符
// @param    const std::string & input [in] MAC地址字符串
// @param    std::string & mac [out] 成功保存归一化后的MAC地址字符串
// @param    bool debugLog 是否输出调试信息
// @return   bool 成功返回true,否则返回false
//************************************
static bool normalize_nic(const std::string &input, std::string &mac,bool debugLog)
{	
	try
	{
		std::regex pattern("[^\\w\\d]+");
		mac = gdface::toupper(std::regex_replace(input, pattern, ""));
		return true;
	}
	catch (const  std::regex_error & e)
	{
		auto errmsg = std::string(typeid(e).name()) + ":" + e.what();
		SAMPLE_LOG_IF(debugLog, "REGEX error:{}", errmsg);
		return false;
	}
}

bool ifu::get_ip_address(const std::string& ifname, std::string &ip_address, bool debugLog)
{
	SAMPLE_LOG_IF(debugLog, "ifname [{}]", ifname);

 #ifdef _WIN32
	ULONG bufsize = 0;
	if (ERROR_BUFFER_OVERFLOW != GetAdaptersInfo(nullptr, &bufsize))
	{
		SAMPLE_LOG_IF(debugLog,"GetAdaptersInfo Failed! ErrorCode: {}", GetLastError());
		return false;
	}

	std::vector<IP_ADAPTER_INFO> adapterInfo(bufsize/sizeof(IP_ADAPTER_INFO));

	auto status = GetAdaptersInfo(adapterInfo.data(), &bufsize);
	
	if (ERROR_SUCCESS == status)
	{
		if (ifname.empty())
		{
			ip_address = std::string((char*)adapterInfo[0].IpAddressList.IpAddress.String);
			SAMPLE_LOG_IF(debugLog,"found network interface {} IP:{}", adapterInfo[0].AdapterName, ip_address);
			return true;
		}
		else
		{
			/** 遍历所有网卡,查找指定名字的网卡并返回 */ 
			PIP_ADAPTER_INFO pAdapterInfo = &adapterInfo[0];
			while (pAdapterInfo)
			{
				SAMPLE_LOG_IF(debugLog, "---- network interface {}", pAdapterInfo->AdapterName);
				if (0 == std::strncmp(pAdapterInfo->AdapterName, ifname.c_str(), sizeof(pAdapterInfo->AdapterName)))
				{
					ip_address = std::string((char*)pAdapterInfo->IpAddressList.IpAddress.String);
					SAMPLE_LOG_IF(debugLog, "found network interface {} IP:{}", pAdapterInfo->AdapterName,ip_address);
					return true;
				}
				pAdapterInfo = pAdapterInfo->Next;
			}
			SAMPLE_LOG_IF(debugLog, "Not found adapter named: {}", ifname);
		}
	}
	else {
		SAMPLE_LOG_IF(debugLog,"GetAdaptersInfo Failed! ErrorCode: {}", GetLastError());
	}
	return false;
#elif __linux__

	if (!ifname.empty())
	{
		return get_ipaddr(ip_address, ifname, true, debugLog);
	}
	if (get_ipaddr(ip_address,"eth0", true, debugLog))
	{
		return true;
	}
	if (get_ipaddr(ip_address,"wlan0", true, debugLog))
	{
		return true;
	}
	return get_ipaddr(ip_address, ifname, true, debugLog);
#else
	SAMPLE_LOG_IF(debugLog, "UNIMPLEMENTED");
	return false;
#endif
}
bool ifu::get_mac_address(const std::string& ifname, std::string &mac_address, bool debugLog)
{
	SAMPLE_LOG_IF(debugLog, "ifname [{}]", ifname);
#ifdef _WIN32
	ULONG bufsize = 0;
	if (ERROR_BUFFER_OVERFLOW != GetAdaptersInfo(nullptr, &bufsize))
	{
		SAMPLE_LOG_IF(debugLog, "GetAdaptersInfo Failed! ErrorCode: {}", GetLastError());
		return false;
	}

	std::vector<IP_ADAPTER_INFO> adapterInfo(bufsize / sizeof(IP_ADAPTER_INFO));

	auto status = GetAdaptersInfo(adapterInfo.data(), &bufsize);

	if (ERROR_SUCCESS == status)
	{
		if (ifname.empty())
		{
			auto buf = gdface::bytes_to_hex_string(adapterInfo[0].Address, 6);
			SAMPLE_LOG_IF(debugLog, "found network interface {} MAC:{} ", adapterInfo[0].AdapterName, buf);
			mac_address = buf;
			return true;
		}
		else
		{
			// 遍历所有网卡,查找指定名字的网卡并返回
			PIP_ADAPTER_INFO pAdapterInfo = &adapterInfo[0];
			while (pAdapterInfo)
			{
				SAMPLE_LOG_IF(debugLog, "---- network interface {}", pAdapterInfo->AdapterName);
				if (0 == std::strncmp(pAdapterInfo->AdapterName, ifname.c_str(), sizeof(pAdapterInfo->AdapterName)))
				{
					auto buf = gdface::bytes_to_hex_string(pAdapterInfo->Address,6);
					SAMPLE_LOG_IF(debugLog, "found network interface {} MAC:{}", pAdapterInfo->AdapterName, buf);
					mac_address = buf;
					return true;
				}
				pAdapterInfo = pAdapterInfo->Next;
			}
			SAMPLE_LOG_IF(debugLog, "Not found adapter named: {}", ifname);
		}
	}
	else {
		SAMPLE_LOG_IF(debugLog, "GetAdaptersInfo Failed! ErrorCode: {}", GetLastError());
	}
	return false;
#else
	if (!ifname.empty())
	{
		if (!get_mac_by_pipe(ifname, debugLog, mac_address))
		{
			return get_mac(mac_address,ifname, true, debugLog);
		}
		return true;
	}
	if (get_mac_by_pipe("eth0", debugLog, mac_address))
	{
		return true;
	}
	if (get_mac(mac_address,"eth0", true,debugLog))
	{
		return true;
	}
	if (get_mac_by_pipe("wlan0", debugLog, mac_address))
	{
		return true;
	}
	if (get_mac(mac_address,"wlan0", true,debugLog))
	{
		return true;
	}
	return get_mac(mac_address,"", true,debugLog);
#endif
}

bool ifu::find_nic_by_mac(const std::string &input_mac, std::string& ifname, std::string &foundnics, bool debugLog)
{
	if (input_mac.empty())
	{
		SAMPLE_LOG_IF(debugLog, "input_mac is empty");
		return false;
	}
	std::string mac_address;
	if (!normalize_nic(input_mac, mac_address, debugLog))
	{
		return false;
	}
	SAMPLE_LOG_IF(debugLog, "mac_address [{}]", mac_address);
	ifname = "";
	foundnics = "";
#ifdef _WIN32
	ULONG bufsize = 0;
	if (ERROR_BUFFER_OVERFLOW != GetAdaptersInfo(nullptr, &bufsize))
	{
		SAMPLE_LOG_IF(debugLog, "GetAdaptersInfo Failed! ErrorCode: {}", GetLastError());
		return false;
	}

	std::vector<IP_ADAPTER_INFO> adapterInfo(bufsize / sizeof(IP_ADAPTER_INFO));

	auto status = GetAdaptersInfo(adapterInfo.data(), &bufsize);

	if (ERROR_SUCCESS == status)
	{
		// 遍历所有网卡,查找指定MAC地址的网卡并返回
		PIP_ADAPTER_INFO pAdapterInfo = &adapterInfo[0];
		
		while (pAdapterInfo)
		{
			auto buf = gdface::bytes_to_hex_string(pAdapterInfo->Address, 6);
			foundnics.append(pAdapterInfo->AdapterName).append("/").append(buf);
			SAMPLE_LOG_IF(debugLog, "found network interface {} {}", pAdapterInfo->AdapterName, buf);
			std::string m = buf;
			if (m == mac_address)
			{
				ifname = pAdapterInfo->AdapterName;				
			}
			pAdapterInfo = pAdapterInfo->Next;
			if (pAdapterInfo)
			{
				foundnics.append(",");
			}
		}	
		SAMPLE_LOG_IF(debugLog && ifname.empty(), "Not found adapter named: {},found nic:", ifname,foundnics);
		return !ifname.empty();
	}
	else {
		SAMPLE_LOG_IF(debugLog, "GetAdaptersInfo Failed! ErrorCode: {}", GetLastError());
	}
	return false;
#else
	std::string output;
	/* 列出所有网卡设备名 */
	if (shell_command("ls /sys/class/net 2>/dev/null", debugLog, output))
	{
		return false;
	}
	/* 排除 lo 的所有网卡设备名称 */
	auto ifnames = std::regex_replace(output, std::regex("lo"), "");
	auto vifnames = split(ifnames, "\\s+");
	if (vifnames.empty())
	{
		SAMPLE_LOG_IF(debugLog, "NOT FOUND NET INTERFACE");
		return false;
	}
	SAMPLE_LOG_IF(debugLog, "FOUND NET INTERFACE:{}", ifnames);
	for ( int i=0; i<vifnames.size(); ++i)
	{
		auto name = vifnames[i];
		std::string m;
		if (!get_mac_by_cat_sys_class_net(name, debugLog, m))
		{
			continue;
		}
		foundnics.append(ifname).append("/").append(m);
		if (i < vifnames.size() - 1)
		{
			foundnics.append(",");
		}
		if (m == mac_address)
		{
			ifname = name;
		}
	}
	SAMPLE_LOG_IF(debugLog && ifname.empty(), "Not found adapter named: {},found nic:", ifname, foundnics);
	return !ifname.empty();
#endif
}

bool ifu::check_mac_matched(const std::string &mac, bool debugFlag, std::string &errmsg)
{
	std::string ifname;
	std::string foundnics;
	/* 检查指定MAC地址是否为本机的MAC地址 */
	if (!find_nic_by_mac(mac, ifname, foundnics, debugFlag))
	{
		errmsg = gdface::log::sample_format("device MAC {} of license file mismatch with current device,found nics:{}", mac, foundnics);
		SAMPLE_LOG_IF(debugFlag,"error {}", errmsg);
		return false;
	}
	SAMPLE_LOG_IF(debugFlag,"net interface device with mac {} named {}", mac, ifname);
	return true;
}
